﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.IO;

using System.ComponentModel;
using System.Net;
using System.Net.Sockets;
using System.Threading;

using System.ComponentModel;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Data;
using System.Xml;

namespace Client
{
    public static class formController
    {
        // Needed to update the form with messages from another thread
        delegate void UpdateLogCallback(string strMessage);

        // Needed to set the form to a "disconnected" state from another thread
        delegate void CloseConnectionCallback(string strReason);

        // Server communication 
        public static Thread thrMessaging;
        public static IPAddress ipAddr;
        public static Boolean Connected;
        public static StreamWriter swSender;
        public static StreamReader srReceiver;
        public static TcpClient tcpServer;

        public static String errorMessage;
        public static String receivedProtocol;
        public static String receivedMessage;

        // Synchronises the thread with the form and runs correct protocol
        public static Boolean newProtocol; 

        // Will hold the current file object
        public static XFile xfile;
        
        // Will hold the logged in user and the current user being edited
        public static User user;
        public static User edituser;

        // Will hold a list of users and files
        public static DataTable userlist;
        public static DataTable xfilelist;

        // WiLL hold a list of forms
        private static List<Window> forms;

        static formController()
        {
            forms = new List<Window>();

            forms.Add(new mainWindow());
            forms.Add(new newUser());
            forms.Add(new saunaMain());
            forms.Add(new AccountInfo());
            forms.Add(new AboutSauna());

            xfile = new XFile();

            user = new User();
            edituser = new User();
        }


        // FORM MANAGEMENT

        public static void ShowForm(System.Type formName)
        {
            
            //Loop through the forms and check for an instance
            foreach (Window form in forms)
            {
                if (form.GetType() == formName)
                {

                    form.Show();

                    // Loads user and file data into the main form
                    if (formName.FullName == "Client.saunaMain")
                    {
                        formController.GetUserList(formController.user.username);
                        formController.ListFiles();
                    }
                }
                else
                {
                    form.Hide();
                }
            }
        }

        private static Window GetFormInstance(System.Type formName)
        {
            //Loop through the forms and check for an instance
            foreach (Window form in forms)
            {
                if (form.GetType() == formName)
                {
                    return form;
                }
            }

            return null;
        }


        // USER MANAGEMENT

        public static void CreateUser(String username, String password, String firstname, String lastname, String email, String phone)
        {
            InitializeConnection(username);

            swSender.WriteLine(Protocol.Command(SaunaProtocol.CreateUser, username + "," + password + "," + firstname + "," + lastname + "," + email + "," + phone));
            swSender.Flush();
        }

        public static void Login(String username, String password)
        {
            InitializeConnection(username);
            user.username = username;

            SendMessage(Protocol.Command(SaunaProtocol.Login, username + "," + password));
            //swSender.Flush();
        }

        public static void Logout(String username)
        {
            //SendMessage(Protocol.Command(SaunaProtocol.Logout, username));
            thrMessaging.Abort(); // Temp
            CloseConnection(errorMessage); // Temp
        }

        public static void GetProfile(String username)
        {
            SendMessage(Protocol.Command(SaunaProtocol.GetProfile, username));
        }

        public static void UpdateProfile(String username, String firstname, String lastname, String password, String email, String phone)
        {
            SendMessage(Protocol.Command(SaunaProtocol.UpdateProfile, username + "," + firstname + "," + lastname + "," + password + "," + email + "," + phone));
        }

        public static void DeleteUser(String username)
        {
            SendMessage(Protocol.Command(SaunaProtocol.DeleteUser, username));
        }

        public static void GetUserList(String username)
        {
            SendMessage(Protocol.Command(SaunaProtocol.GetUserList, username));
        }

        public static void ListFriends(String username)
        {
            SendMessage(Protocol.Command(SaunaProtocol.ListFriends, username));
        }

        public static void EditAccount()
        {
            formController.ShowForm(typeof(AccountInfo));
            AccountInfo accountInfoForm = (AccountInfo)GetFormInstance(typeof(AccountInfo));
            accountInfoForm.tbxName.Text = "";
            accountInfoForm.tbxPass.Text = "";
            accountInfoForm.tbxEmail.Text = "";
            accountInfoForm.tbxPhone.Text = "";
            accountInfoForm.tbxFirstName.Text = "";
            accountInfoForm.tbxLastName.Text = "";

            accountInfoForm.tbxName.Text = edituser.username;
            accountInfoForm.tbxFirstName.Text = edituser.firstname;
            accountInfoForm.tbxLastName.Text = edituser.lastname;
            accountInfoForm.tbxPass.Text = edituser.password;
            accountInfoForm.tbxPhone.Text = edituser.phone;
            accountInfoForm.tbxEmail.Text = edituser.email;
        }

        public static void EditAccount(User userid)
        {
            formController.ShowForm(typeof(AccountInfo));
            AccountInfo accountInfoForm = (AccountInfo)GetFormInstance(typeof(AccountInfo));
            accountInfoForm.tbxName.Text = "";
            accountInfoForm.tbxPass.Text = "";
            accountInfoForm.tbxEmail.Text = "";
            accountInfoForm.tbxPhone.Text = "";
            accountInfoForm.tbxFirstName.Text = "";
            accountInfoForm.tbxLastName.Text = "";

            accountInfoForm.tbxName.Text = edituser.username;
            accountInfoForm.tbxFirstName.Text = edituser.firstname;
            accountInfoForm.tbxLastName.Text = edituser.lastname;
            accountInfoForm.tbxPass.Text = edituser.password;
            accountInfoForm.tbxPhone.Text = edituser.phone;
            accountInfoForm.tbxEmail.Text = edituser.email;

        }


        // FILE MANAGEMENT

        public static void ListFiles()
        {
            SendMessage(Protocol.Command(SaunaProtocol.ListFiles, ""));
        }

        public static void FileExists()
        {
            SendMessage(Protocol.Command(SaunaProtocol.FileExists, xfile.xfilename));
        }

        public static void AddFile()
        {
            SendMessage(Protocol.Command(SaunaProtocol.AddFile, xfile.xfilename));

            Stream Fs = File.OpenRead(xfile.xfilepath);
            Byte[] buffer = new Byte[Fs.Length];
            Fs.Read(buffer, 0, buffer.Length);
            TcpClient socket = new TcpClient();
            socket.Connect(ipAddr, 1095);
            NetworkStream nw = socket.GetStream();
            nw.Write(buffer, 0, buffer.Length);
            nw.Close();
            //swSender.WriteLine("$SAU,AFDONE," + strArray[strArray.Length - 1]); swSender.WriteLine("$SAU,AF," + strArray[strArray.Length - 1]);
            //swSender.Flush();
            //this.Close();
            SendMessage(Protocol.Command(SaunaProtocol.AddFile, "DONE"));
            SendMessage(Protocol.Command(SaunaProtocol.NameFile, xfile.xfilename));
        }

        public static void DeleteFile(String filename)
        {
            SendMessage(Protocol.Command(SaunaProtocol.DeleteFile, filename));
        }



        // SERVER COMMUNICATION

        public static void InitializeConnection(String strMessage)
        {
            // Parse the IP address from the TextBox into an IPAddress object
            ipAddr = IPAddress.Parse("127.0.0.1");
            //ipAddr = IPAddress.Parse("192.168.1.107");
            //ipAddr = IPAddress.Parse("137.219.172.46");

            // Start a new TCP connections to the chat server
            tcpServer = new TcpClient();
            tcpServer.Connect(ipAddr, 1986);

            // Helps us track whether we're connected or not
            Connected = true;

            // Send the desired username to the server
            swSender = new StreamWriter(tcpServer.GetStream());
            swSender.WriteLine(strMessage);
            swSender.Flush();

            // Start the thread for receiving messages and further communication
            thrMessaging = new Thread(new ThreadStart(ReceiveMessages));
            thrMessaging.Start();
        }

        private static void ReceiveMessages()
        {

            // Receive the response from the server
            srReceiver = new StreamReader(tcpServer.GetStream());

            // If the first character of the response is 1, connection was successful
            string ConResponse = srReceiver.ReadLine();

            // If the first character is a 1, connection was successful
            if (ConResponse[0] == '1')
            {
                // Update the form to tell it we are now connected
                UpdateLogCallback logCallBack = new UpdateLogCallback(UpdateLog);
                logCallBack("Connected Successfully!");
            }
            else // If the first character is not a 1 (probably a 0), the connection was unsuccessful
            {
                string Reason = "Not Connected: ";

                // Extract the reason out of the response message. The reason starts at the 3rd character
                Reason += ConResponse.Substring(2, ConResponse.Length - 2);

                // Update the form with the reason why we couldn't connect
                CloseConnectionCallback connectionCallback = new CloseConnectionCallback(CloseConnection);
                connectionCallback(Reason);

                // Exit the method
                return;
            }

            // While we are successfully connected, read incoming lines from the server
            while (Connected)
            {
                // Show the messages in the log TextBox
                UpdateLogCallback logCallBack = new UpdateLogCallback(UpdateLog);
                logCallBack(srReceiver.ReadLine());
            }
        }

        private static void UpdateLog(string strMessage)
        {
            try
            {
                receivedProtocol = strMessage.Split('$')[1].Split(',')[1];

                try
                {
                    receivedMessage = strMessage.Split('$')[1].Split(',')[2];
                }
                catch { }

                switch (receivedProtocol)
                {

                    #region Login Result

                    case "LR":
                        {
                            if (receivedMessage == "OK")
                            {
                                newProtocol = true;
                            }
                            else
                            {
                                errorMessage = "Your login attempt was not successful. Please try again";

                                newProtocol = true;

                                CloseConnection(errorMessage);
                            }
                        }
                        break;

                    #endregion

                    #region Logout

                    case "LO":
                        {
                            if (receivedMessage == "OK")
                            {
                                newProtocol = true;
                            }
                            else
                            { 
                                newProtocol = true;

                                CloseConnection(errorMessage);
                            }
                        }
                        break;

                    #endregion

                    #region User Created

                    case "UC":
                        {
                            if (receivedMessage == "OK")
                            {
                                newProtocol = true;
                            }
                            else
                            {
                                errorMessage = "Please check you have entered the correct details and try again";

                                newProtocol = true;

                                CloseConnection(errorMessage);
                            }
                        }
                        break;

                    #endregion

                    #region User Exists

                    case "UE":
                        {
                            errorMessage = "The user name entered already exists please try again";

                            newProtocol = true;

                            CloseConnection(errorMessage);
                        }
                        break;

                    #endregion

                    #region Get User List
                    
                    case "GUL":
                        {
                            userlist = new DataTable();
                            String xml = strMessage.Split('$')[1].Split(',')[3];

                            userlist.Columns.Add("USERID", System.Type.GetType("System.String"));


                            try
                            {
                                StringReader stream = new StringReader(xml);

                                String element = String.Empty;

                                using (XmlTextReader reader = new XmlTextReader(stream))
                                {
                                    while (reader.Read())
                                    {
                                        if (reader.NodeType == XmlNodeType.Element)
                                        {
                                            element = reader.Name;
                                        }
                                        else if (reader.NodeType == XmlNodeType.Text)
                                        {
                                            switch (element)
                                            {
                                                case "USERID":

                                                    //Console.WriteLine(reader.Value + Environment.NewLine);
                                                    userlist.Rows.Add(reader.Value);
                                                    break;

                                                default:

                                                    break;
                                            }
                                        }
                                    }
                                }
                            }
                            catch (Exception ex)
                            {

                            }

                            newProtocol = true;

                        }
                        break;

                    #endregion

                    #region Get Profile

                    case "GP":
                        {
                            String xml = strMessage.Split('$')[1].Split(',')[3];

                            Console.WriteLine(receivedMessage);

                            try
                            {
                                StringReader stream = new StringReader(xml);

                                String element = String.Empty;

                                using (XmlTextReader reader = new XmlTextReader(stream))
                                {
                                    while (reader.Read())
                                    {
                                        if (reader.NodeType == XmlNodeType.Element)
                                        {
                                            element = reader.Name;
                                        }
                                        else if (reader.NodeType == XmlNodeType.Text)
                                        {
                                            switch (element)
                                            {
                                                case "USERID":

                                                    //Console.WriteLine(reader.Value + Environment.NewLine);
                                                    edituser.username = reader.Value;
                                                    break;

                                                case "FIRSTNAME":

                                                    //Console.WriteLine(reader.Value + Environment.NewLine);
                                                    edituser.firstname = reader.Value;
                                                    break;

                                                case "LASTNAME":

                                                    //Console.WriteLine(reader.Value + Environment.NewLine);
                                                    edituser.lastname = reader.Value;
                                                    break;

                                                case "PHONE":

                                                    //Console.WriteLine(reader.Value + Environment.NewLine);
                                                    edituser.phone = reader.Value;
                                                    break;

                                                case "EMAIL":

                                                    //Console.WriteLine(reader.Value + Environment.NewLine);
                                                    edituser.email = reader.Value;
                                                    break;

                                                default:

                                                    break;
                                            }
                                        }
                                    }
                                }
                            }
                            catch (Exception ex)
                            {

                            }

                            newProtocol = true;
                        }
                        break;

                    #endregion

                    #region User Deleted

                    case "UD":
                        {
                            if (receivedMessage == "OK")
                            {
                                newProtocol = true;
                            }
                            else
                            {
                                errorMessage = "The user was not deleted. Please try again";

                                newProtocol = true;
                            }
                        }
                        break;

                    #endregion 

                    #region Update Profile

                    case "UP":
                        {
                            newProtocol = true;
                        }
                        break;

                    #endregion 

                    #region Name File

                    case "NF":
                        {
                            if (receivedMessage == "OK")
                            {
                                newProtocol = true;
                            }
                            else
                            {
                                errorMessage = "File transfer failed please try again!";

                                newProtocol = true;
                            }
                        }
                        break;

                    #endregion

                    #region File Exists

                    case "FE":
                        {
                            if (receivedMessage == "OK")
                            {
                                errorMessage = "The file already exists on the server please delete and re upload the file!";

                                newProtocol = true;
                            }
                            else
                            {
                                newProtocol = true;
                            }
                        }
                        break;

                    #endregion

                    #region List Files

                    case "LF":
                        {
                            xfilelist = new DataTable();
                            String xml = strMessage.Split('$')[1].Split(',')[2];

                            xfilelist.Columns.Add("FILENAME", System.Type.GetType("System.String"));


                            try
                            {
                                StringReader stream = new StringReader(xml);

                                String element = String.Empty;

                                using (XmlTextReader reader = new XmlTextReader(stream))
                                {
                                    while (reader.Read())
                                    {
                                        if (reader.NodeType == XmlNodeType.Element)
                                        {
                                            element = reader.Name;
                                        }
                                        else if (reader.NodeType == XmlNodeType.Text)
                                        {
                                            switch (element)
                                            {
                                                case "FILENAME":

                                                    //Console.WriteLine(reader.Value + Environment.NewLine);
                                                    xfilelist.Rows.Add(reader.Value);
                                                    break;

                                                default:

                                                    break;
                                            }
                                        }
                                    }
                                }
                            }
                            catch (Exception ex)
                            {

                            }

                            newProtocol = true;
                        }
                        break;

                    #endregion

                    #region Delete File

                    case "DF":
                        {
                            if (receivedMessage == "OK")
                            {
                                newProtocol = true;
                            }
                            else
                            {
                                errorMessage = "The file was not deleted. Please try again";

                                newProtocol = true;
                            }
                        }
                        break;

                    #endregion 

                    default:

                        break;
                }
            }
            catch { Console.WriteLine( "Non SAU message recived: " + strMessage); }

        }

        private static void CloseConnection(string Reason)
        {
            // Close the objects
            Connected = false;
            swSender.Close();
            srReceiver.Close();
            tcpServer.Close();
        }

        public static void SendMessage(string message)
        {
            if (message.Length >= 1)
            {
                swSender.WriteLine(message);
                swSender.Flush();
                message = null;
            }

            message = "";
        }
    }
}
